extends layout

block append style
  link(rel="stylesheet", href="./css/inlinecpc.css")
  script(type="text/javascript" src="./js/native.js")

block content
  :markdown
    <h2 id="schematypes"><a href="#schematypes">SchemaTypes</a></h2>

    <script>
      _native.init("CK7DT53U",{
        targetClass: 'native-inline'
      });
    </script>

    <div class="native-inline">
      <a href="#native_link#"><span class="sponsor">Sponsor</span> #native_company# — #native_desc#</a>
    </div>

    SchemaTypes handle definition of path
    [defaults](./api.html#schematype_SchemaType-default),
    [validation](./api.html#schematype_SchemaType-validate),
    [getters](#getters),
    [setters](./api.html#schematype_SchemaType-set),
    [field selection defaults](./api.html#schematype_SchemaType-select) for
    [queries](./api.html#query-js),
    and other general characteristics for Mongoose document properties.

    <ul class="toc">
      <li><a href="#what-is-a-schema-type">What is a SchemaType?</a></li>
      <li><a href="#schematype-options">SchemaType Options</a></li>
      <li><a href="#customtypes">Creating Custom Types</a></li>
      <li><a href="#path">The `schema.path()` Function</a></li>
    </ul>

    <h3 id="what-is-a-schematype"><a href="#what-is-a-schematype">What is a SchemaType?</a></h3>

    You can think of a Mongoose schema as the configuration object for a
    Mongoose model. A SchemaType is then a configuration object for an individual
    property. A SchemaType says what type a given
    path should have, whether it has any getters/setters, and what values are
    valid for that path.

    ```javascript
    const schema = new Schema({ name: String });
    schema.path('name') instanceof mongoose.SchemaType; // true
    schema.path('name') instanceof mongoose.Schema.Types.String; // true
    schema.path('name').instance; // 'String'
    ```

    A SchemaType is different from a type. In other words, `mongoose.ObjectId !== mongoose.Types.ObjectId`.
    A SchemaType is just a configuration object for Mongoose. An instance of
    the `mongoose.ObjectId` SchemaType doesn't actually create MongoDB ObjectIds,
    it is just a configuration for a path in a schema.

    The following are all the valid SchemaTypes in Mongoose. Mongoose plugins
    can also add custom SchemaTypes like [int32](http://plugins.mongoosejs.io/plugins/int32).
    Check out [Mongoose's plugins search](http://plugins.mongoosejs.io) to find plugins.

    - [String](#strings)
    - [Number](#numbers)
    - [Date](#dates)
    - [Buffer](#buffers)
    - [Boolean](#booleans)
    - [Mixed](#mixed)
    - [ObjectId](#objectids)
    - [Array](#arrays)
    - [Decimal128](./api.html#Decimal128)
    - [Map](#maps)

    <h4>Example</h4>

    ```javascript
    var schema = new Schema({
      name:    String,
      binary:  Buffer,
      living:  Boolean,
      updated: { type: Date, default: Date.now },
      age:     { type: Number, min: 18, max: 65 },
      mixed:   Schema.Types.Mixed,
      _someId: Schema.Types.ObjectId,
      decimal: Schema.Types.Decimal128,
      array: [],
      ofString: [String],
      ofNumber: [Number],
      ofDates: [Date],
      ofBuffer: [Buffer],
      ofBoolean: [Boolean],
      ofMixed: [Schema.Types.Mixed],
      ofObjectId: [Schema.Types.ObjectId],
      ofArrays: [[]],
      ofArrayOfNumbers: [[Number]],
      nested: {
        stuff: { type: String, lowercase: true, trim: true }
      },
      map: Map,
      mapOfString: {
        type: Map,
        of: String
      }
    })

    // example use

    var Thing = mongoose.model('Thing', schema);

    var m = new Thing;
    m.name = 'Statue of Liberty';
    m.age = 125;
    m.updated = new Date;
    m.binary = Buffer.alloc(0);
    m.living = false;
    m.mixed = { any: { thing: 'i want' } };
    m.markModified('mixed');
    m._someId = new mongoose.Types.ObjectId;
    m.array.push(1);
    m.ofString.push("strings!");
    m.ofNumber.unshift(1,2,3,4);
    m.ofDates.addToSet(new Date);
    m.ofBuffer.pop();
    m.ofMixed = [1, [], 'three', { four: 5 }];
    m.nested.stuff = 'good';
    m.map = new Map([['key', 'value']]);
    m.save(callback);
    ```

    <h3 id="schematype-options"><a href="#schematype-options">SchemaType Options</a></h3>

    You can declare a schema type using the type directly, or an object with
    a `type` property.

    ```javascript
    var schema1 = new Schema({
      test: String // `test` is a path of type String
    });

    var schema2 = new Schema({
      // The `test` object contains the "SchemaType options"
      test: { type: String } // `test` is a path of type string
    });
    ```

    In addition to the type property, you can specify additional properties
    for a path. For example, if you want to lowercase a string before saving:

    ```javascript
    var schema2 = new Schema({
      test: {
        type: String,
        lowercase: true // Always convert `test` to lowercase
      }
    });
    ```

    You can add any property you want to your SchemaType options. Many plugins
    rely on custom SchemaType options. For example, the [mongoose-autopopulate](http://plugins.mongoosejs.io/plugins/autopopulate)
    plugin automatically populates paths if you set `autopopulate: true` in your
    SchemaType options. Mongoose comes with support for several built-in
    SchemaType options, like `lowercase` in the above example.

    The `lowercase` option only works for strings. There are certain options
    which apply for all schema types, and some that apply for specific schema
    types.

    <h5>All Schema Types</h5>

    * `required`: boolean or function, if true adds a [required validator](./validation.html#built-in-validators) for this property
    * `default`: Any or function, sets a default value for the path. If the value is a function, the return value of the function is used as the default.
    * `select`: boolean, specifies default [projections](https://docs.mongodb.com/manual/tutorial/project-fields-from-query-results/) for queries
    * `validate`: function, adds a [validator function](./validation.html#built-in-validators) for this property
    * `get`: function, defines a custom getter for this property using [`Object.defineProperty()`](https://developer.mozilla.org/en-US./Web/JavaScript/Reference/Global_Objects/Object/defineProperty).
    * `set`: function, defines a custom setter for this property using [`Object.defineProperty()`](https://developer.mozilla.org/en-US./Web/JavaScript/Reference/Global_Objects/Object/defineProperty).
    * `alias`: string, mongoose >= 4.10.0 only. Defines a [virtual](./guide.html#virtuals) with the given name that gets/sets this path.

    ```javascript
    var numberSchema = new Schema({
      integerOnly: {
        type: Number,
        get: v => Math.round(v),
        set: v => Math.round(v),
        alias: 'i'
      }
    });

    var Number = mongoose.model('Number', numberSchema);

    var doc = new Number();
    doc.integerOnly = 2.001;
    doc.integerOnly; // 2
    doc.i; // 2
    doc.i = 3.001;
    doc.integerOnly; // 3
    doc.i; // 3
    ```

    <h5>Indexes</h5>

    You can also define [MongoDB indexes](https://docs.mongodb.com/manual/indexes/)
    using schema type options.

    * `index`: boolean, whether to define an [index](https://docs.mongodb.com/manual/indexes/) on this property.
    * `unique`: boolean, whether to define a [unique index](https://docs.mongodb.com/manual/core/index-unique/) on this property.
    * `sparse`: boolean, whether to define a [sparse index](https://docs.mongodb.com/manual/core/index-sparse/) on this property.

    ```javascript
    var schema2 = new Schema({
      test: {
        type: String,
        index: true,
        unique: true // Unique index. If you specify `unique: true`
        // specifying `index: true` is optional if you do `unique: true`
      }
    });
    ```

    <h5 id="string-validators">String</h5>

    * `lowercase`: boolean, whether to always call `.toLowerCase()` on the value
    * `uppercase`: boolean, whether to always call `.toUpperCase()` on the value
    * `trim`: boolean, whether to always call `.trim()` on the value
    * `match`: RegExp, creates a [validator](./validation.html) that checks if the value matches the given regular expression
    * `enum`: Array, creates a [validator](./validation.html) that checks if the value is in the given array.
    * `minlength`: Number, creates a [validator](./validation.html) that checks if the value length is not less than the given number
    * `maxlength`: Number, creates a [validator](./validation.html) that checks if the value length is not greater than the given number

    <h5 id="number-validators">Number</h5>

    * `min`: Number, creates a [validator](./validation.html) that checks if the value is greater than or equal to the given minimum.
    * `max`: Number, creates a [validator](./validation.html) that checks if the value is less than or equal to the given maximum.

    <h5>Date</h5>

    * `min`: Date
    * `max`: Date

    <h3 id="usage-notes"><a href="#usage-notes">Usage notes</a></h3>

    <h4 id="strings">String</h4>

    To declare a path as a string, you may use either the `String` global
    constructor or the string `'String'`.

    ```javascript
    const schema1 = new Schema({ name: String }); // name will be cast to string
    const schema2 = new Schema({ name: 'String' }); // Equivalent

    const Person = mongoose.model('Person', schema2);
    ```

    If you pass an element that has a `toString()` function, Mongoose will call it,
    unless the `toString()` function is strictly equal to `Object.prototype.toString()`.

    ```javascript
    new Person({ name: 42 }).name; // "42" as a string
    new Person({ name: { toString: () => 42 } }).name; // "42" as a string

    // "undefined", will get a cast error if you `save()` this document 
    new Person({ name: { foo: 42 } }).name; 
    ```

    <h4 id="numbers">Number</h4>

    To declare a path as a number, you may use either the `Number` global
    constructor or the string `'Number'`.

    ```javascript
    const schema1 = new Schema({ age: Number }); // age will be cast to a Number
    const schema2 = new Schema({ age: 'Number' }); // Equivalent

    const Car = mongoose.model('Car', schema2);
    ```

    There are several types of values that will be successfully cast to a Number.

    ```javascript
    new Car({ age: '15' }).age; // 15 as a Number
    new Car({ age: true }).age; // 1 as a Number
    new Car({ age: false }).age; // 0 as a Number
    new Car({ age: { valueOf: () => 83 } }).age; // 83 as a Number
    ```

    If you pass an object with a `valueOf()` function that returns a Number, Mongoose will
    call it and assign the returned value to the path.

    The values `null` and `undefined` are not cast.

    NaN, strings that cast to NaN, arrays, and objects that don't have a `valueOf()` function
    will all result in a [CastError](./api.html#mongooseerror_MongooseError.CastError).

    <h4 id="dates">Dates</h4>

    [Built-in `Date` methods](https://developer.mozilla.org/en-US./Web/JavaScript/Reference/Global_Objects/Date) are [__not__ hooked into](https://github.com/Automattic/mongoose/issues/1598) the mongoose change tracking logic which in English means that if you use a `Date` in your document and modify it with a method like `setMonth()`, mongoose will be unaware of this change and `doc.save()` will not persist this modification. If you must modify `Date` types using built-in methods, tell mongoose about the change with `doc.markModified('pathToYourDate')` before saving.

    ```javascript
    var Assignment = mongoose.model('Assignment', { dueDate: Date });
    Assignment.findOne(function (err, doc) {
      doc.dueDate.setMonth(3);
      doc.save(callback); // THIS DOES NOT SAVE YOUR CHANGE

      doc.markModified('dueDate');
      doc.save(callback); // works
    })
    ```

    <h4 id="buffers">Buffer</h4>

    To declare a path as a Buffer, you may use either the `Buffer` global
    constructor or the string `'Buffer'`.

    ```javascript
    const schema1 = new Schema({ binData: Buffer }); // binData will be cast to a Buffer
    const schema2 = new Schema({ binData: 'Buffer' }); // Equivalent

    const Data = mongoose.model('Data', schema2);
    ```

    Mongoose will successfully cast the below values to buffers.

    ```
    const file1 = new Data({ binData: 'test'}); // {"type":"Buffer","data":[116,101,115,116]}
    const file2 = new Data({ binData: 72987 }); // {"type":"Buffer","data":[27]}
    const file4 = new Data({ binData: { type: 'Buffer', data: [1, 2, 3]}}); // {"type":"Buffer","data":[1,2,3]}
    ```

    <h4 id="mixed">Mixed</h4>

    An "anything goes" SchemaType. Mongoose will not do any casting on mixed paths.
    You can define a mixed path using `Schema.Types.Mixed` or by passing an empty
    object literal. The following are equivalent.

    ```javascript
    const Any = new Schema({ any: {} });
    const Any = new Schema({ any: Object });
    const Any = new Schema({ any: Schema.Types.Mixed });
    const Any = new Schema({ any: mongoose.Mixed });
    // Note that if you're using `type`, putting _any_ POJO as the `type` will
    // make the path mixed.
    const Any = new Schema({
      any: {
        type: { foo: String }
      }
    });
    ```

    Since it is a schema-less type, you can change the value to anything else you
    like, but Mongoose loses the ability to auto detect and save those changes.
    To tell Mongoose that the value of a Mixed type has changed, you need to
    call `doc.markModified(path)`, passing the path to the Mixed type you just changed.

    ```javascript
    person.anything = { x: [3, 4, { y: "changed" }] };
    person.markModified('anything');
    person.save(); // Mongoose will save changes to `anything`.
    ```

    <h4 id="objectids">ObjectIds</h4>

    To specify a type of ObjectId, use `Schema.Types.ObjectId` in your declaration.

    ```javascript
    var mongoose = require('mongoose');
    var ObjectId = mongoose.Schema.Types.ObjectId;
    var Car = new Schema({ driver: ObjectId });
    // or just Schema.ObjectId for backwards compatibility with v2
    ```

    <h4 id="booleans">Boolean</h4>

    Booleans in Mongoose are [plain JavaScript booleans](https://www.w3schools.com/js/js_booleans.asp).
    By default, Mongoose casts the below values to `true`:

    * `true`
    * `'true'`
    * `1`
    * `'1'`
    * `'yes'`

    Mongoose casts the below values to `false`:

    * `false`
    * `'false'`
    * `0`
    * `'0'`
    * `'no'`

    Any other value causes a [CastError](./api.html#mongooseerror_MongooseError.CastError).
    You can modify what values Mongoose converts to true or false using the
    `convertToTrue` and `convertToFalse` properties, which are [JavaScript sets](https://developer.mozilla.org/en-US./Web/JavaScript/Reference/Global_Objects/Set).

    ```javascript
    const M = mongoose.model('Test', new Schema({ b: Boolean }));
    console.log(new M({ b: 'nay' }).b); // undefined

    // Set { false, 'false', 0, '0', 'no' }
    console.log(mongoose.Schema.Types.Boolean.convertToFalse);

    mongoose.Schema.Types.Boolean.convertToFalse.add('nay');
    console.log(new M({ b: 'nay' }).b); // false
    ```

    <h4 id="arrays">Arrays</h4>

    Mongoose supports arrays of [SchemaTypes](./api.html#schema_Schema.Types)
    and arrays of [subdocuments](./subdocs.html). Arrays of SchemaTypes are
    also called _primitive arrays_, and arrays of subdocuments are also called
    _document arrays_.

    ```javascript
    var ToySchema = new Schema({ name: String });
    var ToyBoxSchema = new Schema({
      toys: [ToySchema],
      buffers: [Buffer],
      strings: [String],
      numbers: [Number]
      // ... etc
    });
    ```

    Arrays are special because they implicitly have a default value of `[]` (empty array).

    ```javascript
    var ToyBox = mongoose.model('ToyBox', ToyBoxSchema);
    console.log((new ToyBox()).toys); // []
    ```

    To overwrite this default, you need to set the default value to `undefined`

    ```javascript
    var ToyBoxSchema = new Schema({
      toys: {
        type: [ToySchema],
        default: undefined
      }
    });
    ```

    Note: specifying an empty array is equivalent to `Mixed`. The following all create arrays of
    `Mixed`:

    ```javascript
    var Empty1 = new Schema({ any: [] });
    var Empty2 = new Schema({ any: Array });
    var Empty3 = new Schema({ any: [Schema.Types.Mixed] });
    var Empty4 = new Schema({ any: [{}] });
    ```

    <h4 id="maps">Maps</h4>

    _New in Mongoose 5.1.0_

    A `MongooseMap` is a subclass of the built-in [`Map` class](https://developer.mozilla.org/en-US./Web/JavaScript/Reference/Global_Objects/Map).
    In these docs, we'll use the terms 'map' and `MongooseMap` interchangeably.
    In Mongoose, maps are how you create a nested document with arbitrary keys.

    **Note**: In Mongoose Maps, keys must be strings in order to store the document in MongoDB.

    ```javascript
    const userSchema = new Schema({
      // `socialMediaHandles` is a map whose values are strings. A map's
      // keys are always strings. You specify the type of values using `of`.
      socialMediaHandles: {
        type: Map,
        of: String
      }
    });

    const User = mongoose.model('User', userSchema);
    // Map { 'github' => 'vkarpov15', 'twitter' => '@code_barbarian' }
    console.log(new User({
      socialMediaHandles: {
        github: 'vkarpov15',
        twitter: '@code_barbarian'
      }
    }).socialMediaHandles);
    ```

    The above example doesn't explicitly declare `github` or `twitter` as paths,
    but, since `socialMediaHandles` is a map, you can store arbitrary key/value
    pairs. However, since `socialMediaHandles` is a map, you **must** use
    `.get()` to get the value of a key and `.set()` to set the value of a key.

    ```javascript
    const user = new User({
      socialMediaHandles: {}
    });

    // Good
    user.socialMediaHandles.set('github', 'vkarpov15');
    // Works too
    user.set('socialMediaHandles.twitter', '@code_barbarian');
    // Bad, the `myspace` property will **not** get saved
    user.socialMediaHandles.myspace = 'fail';

    // 'vkarpov15'
    console.log(user.socialMediaHandles.get('github'));
    // '@code_barbarian'
    console.log(user.get('socialMediaHandles.twitter'));
    // undefined
    user.socialMediaHandles.github;

    // Will only save the 'github' and 'twitter' properties
    user.save();
    ```

    Map types are stored as [BSON objects in MongoDB](https://en.wikipedia.org/wiki/BSON#Data_types_and_syntax).
    Keys in a BSON object are ordered, so this means the [insertion order](https://developer.mozilla.org/en-US./Web/JavaScript/Reference/Global_Objects/Map#Description)
    property of maps is maintained.

    <h3 id="getters"><a href="#getters">Getters</a></h3>

    Getters are like virtuals for paths defined in your schema. For example,
    let's say you wanted to store user profile pictures as relative paths and
    then add the hostname in your application. Below is how you would structure
    your `userSchema`:

    ```javascript
    const root = 'https://s3.amazonaws.com/mybucket';

    const userSchema = new Schema({
      name: String,
      picture: {
        type: String,
        get: v => `${root}${v}`
      }
    });

    const User = mongoose.model('User', userSchema);

    const doc = new User({ name: 'Val', picture: '/123.png' });
    doc.picture; // 'https://s3.amazonaws.com/mybucket/123.png'
    doc.toObject({ getters: false }).picture; // '123.png'
    ```

    Generally, you only use getters on primitive paths as opposed to arrays
    or subdocuments. Because getters override what accessing a Mongoose path returns,
    declaring a getter on an object may remove Mongoose change tracking for
    that path.

    ```javascript
    const schema = new Schema({
      arr: [{ url: String }]
    });

    const root = 'https://s3.amazonaws.com/mybucket';

    // Bad, don't do this!
    schema.path('arr').get(v => {
      return v.map(el => Object.assign(el, { url: root + el.url }));
    });

    // Later
    doc.arr.push({ key: String });
    doc.arr[0]; // 'undefined' because every `doc.arr` creates a new array!
    ```

    Instead of declaring a getter on the array as shown above, you should
    declare a getter on the `url` string as shown below. If you need to declare
    a getter on a nested document or array, be very careful!

    ```javascript
    const schema = new Schema({
      arr: [{ url: String }]
    });

    const root = 'https://s3.amazonaws.com/mybucket';

    // Good, do this instead of declaring a getter on `arr`
    schema.path('arr.0.url').get(v => `${root}${v}`);
    ```

    <h3 id="customtypes"><a href="#customtypes">Creating Custom Types</a></h3>

    Mongoose can also be extended with [custom SchemaTypes](customschematypes.html). Search the
    [plugins](http://plugins.mongoosejs.io)
    site for compatible types like
    [mongoose-long](https://github.com/aheckmann/mongoose-long),
    [mongoose-int32](https://github.com/vkarpov15/mongoose-int32),
    and
    [other](https://github.com/aheckmann/mongoose-function)
    [types](https://github.com/OpenifyIt/mongoose-types).

    Read more about creating [custom SchemaTypes here](customschematypes.html).

    <h3 id="path"><a href="#path">The `schema.path()` Function</a></h3>

    The `schema.path()` function returns the instantiated schema type for a
    given path.

    ```javascript
    var sampleSchema = new Schema({ name: { type: String, required: true } });
    console.log(sampleSchema.path('name'));
    // Output looks like:
    /**
     * SchemaString {
     *   enumValues: [],
     *   regExp: null,
     *   path: 'name',
     *   instance: 'String',
     *   validators: ...
     */
     ```

    You can use this function to inspect the schema type for a given path,
    including what validators it has and what the type is.

    <h3 id="next"><a href="#next">Next Up</a></h3>

    Now that we've covered `SchemaTypes`, let's take a look at [Connections](./connections.html).